<script lang="ts" setup>
import {
  reactive,
  ref,
  watch,
  Ref,
  computed,
  onMounted,
  provide,
  useSlots,
} from 'vue';
import {
  ElMessage,
  type UploadInstance,
  type UploadProps,
  type UploadRawFile,
  type FormInstance,
  type FormRules,
} from 'element-plus';

// 定义类型
interface FormItemOption {
  value?: string | number | boolean;
  label?: string;
  disabled?: boolean;
  prop?: string;
  [key: string]: any;
}

interface FormItem {
  options: FormItemOption[];
  span: number;
  type: string;
  prop: string;
  label: string;
  width?: string;
  disabled?: boolean;
  show?: boolean;
  clearable?: boolean;
  multiple?: boolean;
  max?: number;
  reg?: string;
  isTrigger?: boolean;
  option?: FormItemOption[];
  value?: string;
  listprop?: string;
  listType?: string;
  accept?: string;
  limit?: number;
  icon?: string;
  text?: string;
  fileType?: string[];
  fileSize?: number;
  deptNum?: number;
  placeholder: string;
  // 其他可能的属性...
}

interface Props {
  modelValue: Record<string, any>;
  items: FormItem[];
  rules?: FormRules;
  options?: [];
  labelWidth?: string;
  contentWidth?: string;
  gutter: number;
}

const inputType = [
  'input',
  'text',
  'number',
  'chinese',
  'english',
  'mobile',
  'email',
  'password',
  'money',
  'textarea',
];

const mixedInputRef = ref(null);

const props = withDefaults(defineProps<Props>(), {
  modelValue: () => ({}),
  items: () => [],
  rules: () => ({}),
  labelWidth: '110px',
  contentWidth: '100%',
  gutter: 0,
});

const emit = defineEmits(['update:modelValue', 'fun', 'validate']);

const slots = useSlots();
const upload = ref<UploadInstance>();
const formRef = ref<FormInstance>();
const header = ref<Record<string, string>>({});
const files = ref<File | null>(null);

// 使用计算属性获取表单数据
const formData = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value),
});

// 初始化认证头
onMounted(() => {
  header.value = { Authorization: `Bearer ${localStorage.getItem('token')}` };
});

// 输入验证函数
const regFun = (e: Event, item: FormItem) => {
  if (item.reg === 'ch') {
    const target = e.target as HTMLInputElement;
    formData.value[item.prop] = target.value.replace(/[^\u4e00-\u9fa5]/g, '');
  }
};

// 输入框失去焦点处理
const onInputBlur = (_e: Event, prop: string, item: FormItem) => {
  // 去除前后空格
  if (
    formData.value[item.prop] &&
    typeof formData.value[item.prop] === 'string'
  ) {
    formData.value[item.prop] = formData.value[item.prop].trim();
  }

  emit('validate', prop);
};

// 单选框变化处理
const onRadioChange = (prop: string, value: any, item: FormItem) => {
  if (item.isTrigger) {
    emit('fun', 'onRadio', value);
  }

  switch (prop) {
    case 'conditionRelation':
      emit('fun', 'conditionRelation', value);
      break;
  }
};

// 树选择处理
const onTreeSelect = (e: any, prop: string, index: number, item: FormItem) => {
  emit('fun', 'getPost', { value: e.value, prop, index, opIdx: item.deptNum });
};

// 下拉选择变化处理
const onSelectChange = (
  e: any,
  prop: string,
  options: any[],
  valueKey?: string
) => {
  const value = valueKey || 'value';
  const matchedOption = options.find((option) => option[value] === e);

  if (matchedOption) {
    emit('fun', prop, matchedOption);
  } else {
    console.warn(`没有找到与值 ${e} 匹配的选项。`);
  }
};

// 获取表单数据和引用
const getForm = () => {
  return {
    formRef: formRef.value,
    formData: { ...formData.value },
  };
};

// 上传成功处理
const uploadSuccess = (file: UploadRawFile) => {
  if (!file) return false;

  const item = props.items.find((r) => r.type === 'upload');
  if (!item) return false;

  const keyName = item.listprop || item.prop;
  const fileObj = file.raw;

  // 校验文件类型
  if (item.fileType) {
    const fileExtension = fileObj.name.split('.').pop()?.toLowerCase() || '';
    const isTypeOk = item.fileType.some((type) => {
      return fileObj.type.includes(type) || fileExtension.includes(type);
    });

    if (!isTypeOk) {
      ElMessage.error(
        `文件格式不正确, 请上传${item.fileType.join('/')}格式文件!`
      );
      formData.value[keyName] = [];
      return false;
    }
  }

  // 校验文件大小
  if (item.fileSize) {
    const isLt = fileObj.size / 1024 / 1024 < item.fileSize;
    if (!isLt) {
      ElMessage.error(`上传文件大小不能超过 ${item.fileSize} MB!`);
      formData.value[keyName] = [];
      return false;
    }
  }

  files.value = fileObj;
  formData.value[keyName] = [fileObj];
  formRef.value?.clearValidate();
  return true;
};

// 超出上传限制处理
const handleExceed: UploadProps['onExceed'] = (files) => {
  upload.value?.clearFiles();
  const file = files[0] as UploadRawFile;
  upload.value?.handleStart(file);
};

// 重置表单
const resetForm = () => {
  formRef.value?.resetFields();
};

// 清空表单验证
const clearForm = () => {
  formRef.value?.clearValidate();
};

// 验证表单
const validate = () => {
  return formRef.value?.validate();
};

// 提供方法给父组件使用
defineExpose({
  getForm,
  resetForm,
  getFile: () => ({ files: files.value }),
  clearForm,
  validate,
});

const formPageData = ref({
  mixedSelectValue: '',
});

const mixedOptionSelect = (val: string) => {
  formPageData.value.mixedSelectValue = val;
};
setTimeout(() => {
  console.log(mixedInputRef.value);
}, 5000);
</script>

<template>
  <el-form ref="formRef" :model="formData" :rules="rules">
    <el-row>
      <template v-for="(item, index) in items" :key="`form-item-${index}`">
        <el-col
          v-if="item.show !== false"
          :span="item.span || 24"
          :style="{ paddingRight: `${gutter}px` }">
          <el-form-item
            :prop="item.prop || formPageData.mixedSelectValue"
            :label="item.label"
            :style="{ width: item.width || contentWidth }">
            <xt-input
              v-if="inputType.includes(item.type)"
              v-model="formData[item.prop]"
              :type="item.type"
              :placeholder="
                item.disabled
                  ? '该项只读'
                  : item.placeholder || `请输入${item.label}`
              "
              :disabled="item.disabled"
              @keyup="regFun($event, item)"
              @blur="(e: Event) => onInputBlur(e, item.prop, item)"></xt-input>

            <xt-input
              v-if="item.type === 'mixed'"
              v-model="formData"
              :type="item.type"
              :options="item.options || []"
              :placeholder="
                item.disabled
                  ? '该项只读'
                  : item.placeholder || `请输入${item.label}`
              "
              :disabled="item.disabled"
              @keyup="regFun($event, item)"
              @blur="(e: Event) => onInputBlur(e, item.prop, item)"
              @mixedOptionSelect="mixedOptionSelect" />
            <!-- 下拉选择 -->
            <el-select
              v-if="item.type === 'select'"
              v-model="formData[item.prop]"
              :placeholder="item.placeholder || `请选择${item.label}`"
              :multiple="item.multiple"
              :clearable="item.clearable ?? true"
              @change="(e: any) => onSelectChange(e, item.prop, item.option || [], item.value)">
              <el-option
                v-for="(option, optIndex) in item.option || []"
                :key="`option-${optIndex}`"
                :value="option[item.value || 'value'] || option.value"
                :label="option.label" />
            </el-select>

            <!-- 开关 -->
            <el-switch
              v-if="item.type === 'switch'"
              v-model="formData[item.prop]"
              :inactive-value="1"
              :active-value="0" />

            <!-- 单选框组 -->
            <el-radio-group
              v-if="item.type === 'radio'"
              v-model="formData[item.prop]"
              @change="onRadioChange(item.prop, formData[item.prop], item)">
              <el-radio
                v-for="(option, optIndex) in item.option || []"
                :key="`radio-${optIndex}`"
                :label="option.value"
                :value="option.value">
                {{ option.label }}
              </el-radio>
            </el-radio-group>

            <!-- 单个复选框 -->
            <el-checkbox
              v-if="item.type === 'checkboxLine'"
              v-model="formData[item.prop]"
              :label="item.label"
              size="large" />

            <!-- 复选框组 -->
            <el-checkbox-group
              v-if="item.type === 'checkbox'"
              v-model="formData[item.prop]">
              <el-checkbox
                v-for="(option, optIndex) in item.option || []"
                :key="`checkbox-${optIndex}`"
                :label="option.value">
                {{ option.label }}
              </el-checkbox>
            </el-checkbox-group>

            <!-- 树形选择 -->
            <el-tree-select
              v-if="item.type === 'treeselect'"
              v-model="formData[item.prop]"
              :data="item.option || []"
              :render-after-expand="false"
              :expand-on-click-node="false"
              :props="{ label: 'label', children: 'children' }"
              :check-strictly="true"
              :disabled="item.disabled"
              :default-expanded-keys="[0]"
              @node-click="(e: any) => onTreeSelect(e, item.prop, index, item)" />

            <!-- 文件上传 -->
            <el-upload
              v-if="item.type === 'upload'"
              ref="upload"
              action="#"
              :file-list="formData[item.listprop || item.prop]"
              :list-type="item.listType || 'text'"
              :accept="item.accept"
              :auto-upload="false"
              :limit="item.limit || 1"
              :on-exceed="handleExceed"
              :headers="header"
              :on-success="uploadSuccess"
              :on-change="uploadSuccess">
              <el-button :icon="item.icon">
                {{ item.text || item.label }}
              </el-button>
            </el-upload>

            <!-- 插槽 -->
            <div v-if="item.type === 'slot' && slots[item.prop]">
              <slot
                :name="item.prop"
                :prop="item.prop"
                :value="formData[item.prop]"></slot>
            </div>

            <!-- 纯文本显示 -->
            <div v-if="item.type === 'text'">
              {{ formData[item.prop] }}
            </div>
          </el-form-item>
        </el-col>
      </template>
    </el-row>
  </el-form>
</template>

<style scoped>
.pore {
  position: relative;
}

.form-item {
  margin-bottom: 18px;
}

:deep(.el-input-group__prepend .el-select) {
  margin: 0 -20px !important;
}

:deep(.el-input-group__prepend) {
  padding: 0 20px !important;
}
</style>
